home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 May: Tool Chest / Developer CD Series May 1996 (Tool Chest) (Apple Computer) (1996).iso / Sample Code / Snippets / QuickDraw / Restore Screen Cluts / PictDocument.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-11-09  |  73.2 KB  |  2,027 lines  |  [TEXT/MPS ]

  1. /******************************************************************************\
  2. *
  3. * Apple Macintosh Developer Technical Support
  4. *
  5. * Picture Document routines
  6. *
  7. * Program: ColorReset
  8. * File:    PictDocument.c
  9. *
  10. * by:      Forrest Tanaka
  11. *
  12. * Copyright © 1988-1992 Apple Computer, Inc.
  13. * All rights reserved.
  14. *
  15. * This source file is the most important part of this program because it shows
  16. * all the code needed to maintain Picture Documents.  A Picture Document is a
  17. * window which displays an off-screen image and lets you paint into it, and
  18. * these two activities show how to use graphics environments to draw off screen
  19. * and copy the off-screen image to the window on the screen.
  20. *
  21. \******************************************************************************/
  22.  
  23.  
  24. /******************************************************************************\
  25. * Header Files
  26. \******************************************************************************/
  27.  
  28. /* System header files needed by MPW C */
  29. #ifndef THINK_C
  30. #include <Errors.h>
  31. #include <Menus.h>
  32. #include <Resources.h>
  33. #include <Windows.h>
  34. #endif
  35.  
  36. #include <GestaltEqu.h>
  37. #include <Palettes.h>
  38. #include <Picker.h>
  39. #include <QDOffscreen.h>
  40.  
  41. /* Header files specific to this program */
  42. #include "PictDocument.h"
  43. #include "EmergMem.h"
  44. #include "Exceptions.h"
  45. #include "ColorReset.h"
  46. #include "MenuHandler.h"
  47. #include "WindowPositioner.h"
  48.  
  49.  
  50. /******************************************************************************\
  51. * Private Constants
  52. \******************************************************************************/
  53.  
  54. /* Picture Document window constants */
  55. #define rPictDocWindID 128 /* Resource ID of Picture Document WIND resource */
  56. #define rScrollBarID   128 /* Resource ID of scroll bar CNTL resource */
  57. #define kDocWKind      128 /* windowKind field of Picture Document windows */
  58. #define kMinWindowSize 64  /* Minimum size of the window in pixels */
  59.  
  60. /* PICT file constants */
  61. #define kPictFileHeaderSize 512    /* Number of bytes in PICT file header */
  62. #define kFileCreator        'CLIM' /* Creator code for this application */
  63. #define kFileType           'PICT' /* File type for this app’s documents */
  64.  
  65. /* Off-screen Constants */
  66. #define kMaxRowBytes 0x3FFE     /* Maximum number of bytes in a row of pixels */
  67. #define kDefaultRes  0x00480000 /* Default resolution is 72 DPI; Fixed type */
  68. #define kITabRes     4          /* Inverse-table resolution */
  69.  
  70. /* Miscellaneous constants */
  71. #define kPaintRadius 8 /* Radius of a drop of paint in pixels */
  72.  
  73.  
  74. /******************************************************************************\
  75. * Private Types
  76. \******************************************************************************/
  77.  
  78. /* Picture Document information */
  79. typedef struct
  80. {
  81.     GWorldPtr     image;      /* Pointer to image in off-screen GWorld */
  82.     Rect          windowRect; /* Rectangle of window except for scroll bars */
  83.     Rect          destRect;   /* Rectangle of image in window */
  84.     RGBColor      foreground; /* Color of foreground */
  85.     RGBColor      background; /* Color of background */
  86.     ControlHandle vScrollBar; /* Horizontal scroll bar */
  87.     ControlHandle hScrollBar; /* Vertical scroll bar */
  88.     FSSpec        file;       /* File spec of Picture Document’s PICT file */
  89.     short         fileRef;    /* File ref # of Picture Document’s PICT file */
  90.     Boolean       dithering;  /* True if image should be dithered */
  91. } PictDocInfoRec, *PictDocInfoPtr, **PictDocInfoHnd;
  92.  
  93. /* Basic picture information */
  94. typedef struct
  95. {
  96.     CTabHandle colors; /* Handle to color table of deepest pixel map */
  97.     short      depth;  /* Max depth for all pixmaps (in the picture) */
  98.     Rect       frame;  /* Picture frame rect (contains entire picture) */
  99. } PictureInfoRec;
  100.  
  101.  
  102. /******************************************************************************\
  103. * Prototypes of Private Functions
  104. \******************************************************************************/
  105.  
  106. static OSErr CreatePictDoc(
  107.     Rect       *pictureRect,
  108.     short      pictureDepth,
  109.     CTabHandle pictureClut,
  110.     WindowPtr  *retDoc);
  111.  
  112.  
  113. static void ScrollPictDoc(
  114.     WindowPtr docWindow,
  115.     short     hScrollDelta,
  116.     short     vScrollDelta);
  117.  
  118. void ResizePictDoc(
  119.     WindowPtr docWindow);
  120.  
  121. OSErr CreateOffScreen(
  122.     Rect       *bounds,
  123.     short      depth,
  124.     CTabHandle colors,
  125.     CGrafPtr   *retPort,
  126.     GDHandle   *retGDevice);
  127.  
  128. OSErr SetUpPixMap(
  129.     short        depth,
  130.     Rect         *bounds,
  131.     CTabHandle   colors,
  132.     short        bytesPerRow,
  133.     PixMapHandle aPixMap);
  134.  
  135. OSErr CreateGDevice(
  136.     PixMapHandle basePixMap,
  137.     GDHandle     *retGDevice);
  138.  
  139. void DisposeOffScreen(
  140.     CGrafPtr doomedPort,
  141.     GDHandle doomedGDevice);
  142.  
  143. OSErr GetPictureInfo(
  144.     short          pictFileRef,
  145.     PictureInfoRec *retPictInfo);
  146.  
  147.  
  148. OSErr DrawFilePicture(
  149.     short pictFileRef,
  150.     Rect  *destRect);
  151.  
  152.  
  153. OSErr OpenFilePicture(
  154.     short pictFileRef,
  155.     Rect  *sourceRect);
  156.  
  157. OSErr CloseFilePicture(void);
  158.  
  159.  
  160.  
  161. /******************************************************************************\
  162. * Global Variables
  163. \******************************************************************************/
  164.  
  165. PictureInfoRec gPictureInfo;  /* Information about a PICT file */
  166. PicHandle      gSpoolPicture; /* Temporary picture used to spool from PICT file */
  167. QDProcsPtr     gSavedProcs;   /* Saves existing QDProcs; used when spooling out */
  168. CQDProcs       gCustomProcs;  /* Customized bottlenecks to spool to PICT file */
  169. short          gPictFileRef;  /* File reference number of a PICT file */
  170. short          gPictureSize;  /* Size of picture in bytes */
  171.  
  172.  
  173. /******************************************************************************\
  174. * NAME & SYNOPSIS:
  175. * FindPictDoc: Find the Picture Document window for a specific file
  176. *
  177. * PARAMETERS:
  178. * See PictDocument.h
  179. *
  180. * DEFINITION:
  181. * See PictDocument.h
  182. *
  183. * DESCRIPTION:
  184. * Each Picture Document window in the window list is examined to see whether it
  185. * has a corresponding disk file.  The fileRef field of the Picture Document
  186. * information record is non-zero if the Picture Document has a disk file
  187. * associated with it.  If there is a disk file, then EqualFSSpec is called to
  188. * see if the FSSpec that I saved in the document’s info record is the same as
  189. * the one passed in fileSpec.  If they are, then a pointer to the window is
  190. * returned.  Otherwise, the loop goes to the next Picture Document window, if
  191. * there is one.
  192. *
  193. * See ColorReset.h for a definition of EqualFSSpec.
  194. *
  195. * RETURN VALUES:
  196. * See PictDocument.h
  197. \******************************************************************************/
  198.  
  199. WindowPtr FindPictDoc(
  200.     FSSpecPtr fileSpec) /* Specification of file to search for */
  201. {
  202.     WindowPtr      pictDocWindow; /* Pointer to Picture Document being tested */
  203.     PictDocInfoHnd pictDocInfo;   /* Handle to Picture Document info rec */
  204.     Boolean        foundDoc;      /* True if found Pict Doc w/ same FSSpec */
  205.  
  206.     /* Get a pointer to the first Picture Document window in the window list */
  207.     pictDocWindow = NextPictDocWindow( nil );
  208.  
  209.     /* Keep searching until found or at end of list */
  210.     foundDoc = false;
  211.     while (pictDocWindow != nil && !foundDoc)
  212.     {
  213.         /* Only have to check FSSpecs if document has a corresponding file */
  214.         pictDocInfo = (PictDocInfoHnd)GetWRefCon( pictDocWindow );
  215.         if ((**pictDocInfo).fileRef != 0)
  216.         {
  217.             /* See if the Picture Document’s file is same as fileSpec */
  218.             HLock( (Handle)pictDocInfo );
  219.             foundDoc = EqualFSSpec( fileSpec, &(**pictDocInfo).file );
  220.             HUnlock( (Handle)pictDocInfo );
  221.  
  222.             /* If FSSpecs aren’t the same, then go to next Picture Document */
  223.             if (!foundDoc)
  224.                 pictDocWindow = NextPictDocWindow( pictDocWindow );
  225.         }
  226.     }
  227.  
  228.     return pictDocWindow;
  229. }
  230.  
  231.  
  232. /******************************************************************************\
  233. * NAME & SYNOPSIS:
  234. * NextPictDocWindow: Return a pointer to the next Picture Document window
  235. *
  236. * PARAMETERS:
  237. * See PictDocument.h
  238. *
  239. * DEFINITION:
  240. * See PictDocument.h
  241. *
  242. * DESCRIPTION:
  243. * NextPictDocWindow goes through every window in the window list that’s behind
  244. * the window specified by aWindow until either a Picture Document window is
  245. * found or until the end of the window list is reached.  If aWindow is nil, then
  246. * the search starts with the front window.
  247. *
  248. * RETURN VALUES:
  249. * See PictDocument.h
  250. \******************************************************************************/
  251.  
  252. WindowPtr NextPictDocWindow(
  253.     WindowPtr aWindow) /* Window to start search from, or nil if front */
  254. {
  255.     /* Start search from next window, or front window if aWindow is nil */
  256.     if (aWindow == nil)
  257.         aWindow = FrontWindow();
  258.     else
  259.         aWindow = (WindowPtr)((WindowPeek)aWindow)->nextWindow;
  260.  
  261.     /* Search until Picture Document wind found or end of wind list reached */
  262.     while (aWindow != nil && !IsPictDocWindow( aWindow ))
  263.         aWindow = (WindowPtr)((WindowPeek)aWindow)->nextWindow;
  264.  
  265.     return aWindow;
  266. }
  267.  
  268.  
  269. /******************************************************************************\
  270. * NAME & SYNOPSIS:
  271. * IsPictDocWindow: Is a window a Picture Document window?
  272. *
  273. * PARAMETERS:
  274. * See PictDocument.h
  275. *
  276. * DEFINITION:
  277. * See PictDocument.h
  278. *
  279. * DESCRIPTION:
  280. * When I create a window, I store a code for that kind of window in the
  281. * windowKind field.  IsPictDocWindow checks the windowKind field of the given
  282. * window against the constant kDocWKind.  If they’re equal, then aWindow
  283. * must be a Picture Document window.
  284. *
  285. * RETURN VALUES:
  286. * See PictDocument.h
  287. \******************************************************************************/
  288.  
  289. Boolean IsPictDocWindow (
  290.     WindowPtr aWindow) /* Pointer to the window being tested */
  291. {
  292.     return ((WindowPeek)aWindow)->windowKind == kDocWKind;
  293. }
  294.  
  295.  
  296. /******************************************************************************\
  297. * NAME & SYNOPSIS:
  298. * DrawPictDoc: Draw the contents of a Picture Document
  299. *
  300. * PARAMETERS:
  301. * See PictDocument.h
  302. *
  303. * DEFINITION:
  304. * See PictDocument.h
  305. *
  306. * DESCRIPTION:
  307. * The image for Picture Documents is stored in an off-screen GrafPort, and its
  308. * GrafPtr is stored in the information record.  This GrafPtr is retrieved, and
  309. * it’s used to CopyBits the GrafPort to the window.  It’s possible that not all
  310. * of the image can be copied to the window.  For example, part of the window
  311. * might be covered by another window.  In this case, the visRgn of the window
  312. * takes care of clipping out the undesireable parts of the original image.  The
  313. * other case in which part of the image shouldn’t be drawn is if part of the
  314. * image is scrolled out of the window.  The visRgn of the window takes care of
  315. * part of this, but we still have to make sure that the image doesn’t draw
  316. * itself over the scroll bar areas of the window in case the scroll bars are
  317. * hidden, which they are if the window is inactive.  To take care of this, the
  318. * destination rectangle which is the rectangle that the off-screen image is
  319. * copied to, is first intersected with the window rectangle, which is the
  320. * rectangle of the window except for the scroll bars.  This is used as the
  321. * destination rectangle to CopyBits.  Then this rectangle is offset by the top-
  322. * left corner of the original destination rectangle, and this is used as the
  323. * source rectangle to CopyBits.  The off-screen image is then copied to the
  324. * window.
  325. *
  326. * RETURN VALUES:
  327. * See PictDocument.h
  328. \******************************************************************************/
  329.  
  330. void DrawPictDoc(
  331.     WindowPtr docWindow) /* Ptr to Picture Document window being drawn */
  332. {
  333.     PictDocInfoHnd pictDocInfo; /* Handle to Picture Document information rec */
  334.     GWorldPtr      image;       /* Pointer to the off-screen image */
  335.     Rect           destRect;    /* Rectangle to copy image into */
  336.     Rect           sourceRect;  /* Rectangle to copy image from */
  337.     Rect           windowRect;  /* Rect of the window except for scroll bars */
  338.     short          mode;        /* Drawing mode to use */
  339.  
  340.     /* Get a pointer to the off-screen image and the window rectangle */
  341.     pictDocInfo = (PictDocInfoHnd)GetWRefCon( docWindow );
  342.     image = (**pictDocInfo).image;
  343.     windowRect = (**pictDocInfo).windowRect;
  344.  
  345.     /* Calculate the rectangle to copy the image to */
  346.     destRect = (**pictDocInfo).destRect;
  347.     (void)SectRect( &destRect, &windowRect, &destRect );
  348.  
  349.     /* Calculate the rectangle to copy the image from */
  350.     sourceRect = destRect;
  351.     OffsetRect( &sourceRect, -(**pictDocInfo).destRect.left,
  352.             -(**pictDocInfo).destRect.top );
  353.  
  354.     /* Specify the drawing mode to use */
  355.     mode = srcCopy;
  356.     if ((**pictDocInfo).dithering)
  357.         mode |= ditherCopy;
  358.  
  359.     /* Draw the off-screen image to the screen */
  360.     EraseRect( &docWindow->portRect );
  361.     CopyBits( &((GrafPtr)image)->portBits, &docWindow->portBits,
  362.             &sourceRect, &destRect,
  363.             mode, nil );
  364.  
  365.     /* Draw the grow box */
  366.     DrawGrowIcon( docWindow );
  367.  
  368.     /* Draw the scroll bars */
  369.     DrawControls( docWindow );
  370. }
  371.  
  372.  
  373. /******************************************************************************\
  374. * NAME & SYNOPSIS:
  375. * ClickPictDoc: Handle a mouse click in a Picture Document window
  376. *
  377. * PARAMETERS:
  378. * See PictDocument.h
  379. *
  380. * DEFINITION:
  381. * See PictDocument.h
  382. *
  383. * DESCRIPTION:
  384. * The first really important call in this routine is FindControl, which is the
  385. * toolbox call which tells you which control and which part of the control the
  386. * mouse was clicked in.  If it returns zero, then the mouse was clicked outside
  387. * of any control, and so we let the user paint into the document.
  388. *
  389. * Painting into the document is done within a loop which iterates as long as the
  390. * mouse button is held down.  While this is the case, the current mouse position
  391. * is retrieved with a call to GetMouse.  If the mouse position changed from the
  392. * previous iteration’s mouse position, then the mouse position is offset to take
  393. * into account the currently-scrolled position, and then the current port is set
  394. * to the document’s off-screen GrafPort.  Then, PaintOval is called which draws
  395. * a drop of paint into the off-screen GrafPort.
  396. *
  397. * The off-screen image then has to be copied to the window so that the user can
  398. * see the new drop of paint.  This is done by first seeing if the drop of paint
  399. * can actually be seen in the window in the currently-scrolled position.  If it
  400. * can, then the rectangle of the drop of paint is offset to take into account
  401. * the currently-scrolled position.  Then the current port is set to the document
  402. * window, and CopyBits is called to copy the new drop of paint to the document
  403. * window.
  404. *
  405. * Finally, the mouse position is remembered and the loop iterates again if the
  406. * mouse button is still held down.
  407. *
  408. * If FindControl indicates that the mouse click was in the thumb of a scroll
  409. * bar, TrackControl is called which lets the user move the thumb around until
  410. * the user releases the mouse button.  Then, the difference between the old and
  411. * new scroll bar position is calculated which indicates the number of pixels
  412. * that the document must be scrolled.  ScrollPictDoc, defined below, is then
  413. * called to scroll the document’s image, update the scrolled-in part of the
  414. * image, and update the document information record to reflect the new scrolled
  415. * position.
  416. *
  417. * If FindControl indicates that the mouse click was in the up arrow, down arrow,
  418. * page up, or page down areas of either scroll bar, then TrackControl is called
  419. * to scroll the document appropriately.  The ScrollActionProc routine, defined
  420. * below, handles the logistics of scrolling in this case.
  421. *
  422. * RETURN VALUES:
  423. * See PictDocument.h
  424. \******************************************************************************/
  425.  
  426. void ClickPictDoc(
  427.     WindowPtr   docWindow,   /* Picture Document window that was clicked */
  428.     EventRecord *clickEvent) /* Mouse click event */
  429. {
  430.     PictDocInfoHnd pictDocInfo;     /* Handle to Picture Document info rec */
  431.     GWorldPtr      image;           /* Pointer to the off-screen image */
  432.     GDHandle       savedDevice;     /* Saves default GDevice */
  433.     Rect           paintRect;       /* Rectangle to draw paint into */
  434.     Rect           windowPaintRect; /* Rectangle to draw paint into */
  435.     Rect           windowRect;      /* Window rect except for scroll bars */
  436.     Rect           destRect;        /* Rectangle of image in window */
  437.     Point          newPosition;     /* New pos of mouse click; local coords */
  438.     Point          oldPosition;     /* Old pos of mouse click; local coords */
  439.     ControlHandle  scrollBar;       /* Handle to the clicked scroll bar */
  440.     short          partNumber;      /* Part # of clicked part of control */
  441.     short          scrollDelta;     /* Number of pixels to scroll */
  442.  
  443.     savedDevice = GetGDevice();
  444.  
  445.     /* Get information about the Picture Document */
  446.     pictDocInfo = (PictDocInfoHnd)GetWRefCon( docWindow );
  447.     image = (**pictDocInfo).image;
  448.     windowRect = (**pictDocInfo).windowRect;
  449.     destRect = (**pictDocInfo).destRect;
  450.  
  451.     /* Get the mouse click position */
  452.     SetPort( docWindow );
  453.     newPosition = clickEvent->where;
  454.     GlobalToLocal( /*◊*/&newPosition );
  455.  
  456.     /* Find whether the mouse was in a control */
  457.     partNumber = FindControl( newPosition, docWindow, /*<*/&scrollBar );
  458.     switch (partNumber)
  459.     {
  460.         case 0:
  461.             /* Clicked outside of any control; see if it was in the image */
  462.             if (PtInRect( newPosition, &windowRect ))
  463.             {
  464.                 /* Click in image; paint while mouse button is held down */
  465.                 oldPosition.v = oldPosition.h = -32767;
  466.                 while (StillDown())
  467.                 {
  468.                     /* Only do something if the mouse moved */
  469.                     if (!EqualPt( newPosition, oldPosition ))
  470.                     {
  471.                         /* Need to offset paint brush for scrolled position */
  472.                         SubPt( topLeft( destRect ), /*◊*/&newPosition );
  473.                         SetRect( /*<*/&paintRect, newPosition.h - kPaintRadius,
  474.                                 newPosition.v - kPaintRadius,
  475.                                 newPosition.h + kPaintRadius,
  476.                                 newPosition.v + kPaintRadius );
  477.  
  478.                         /* Drop some paint into the off-screen image */
  479.                         SetGWorld( image, nil );
  480.                         PaintOval( &paintRect );
  481.  
  482.                         /* Offset brush back to window position */
  483.                         AddPt( topLeft( destRect ), /*◊*/&newPosition );
  484.  
  485.                         /* Calc rect in window for to brush rect in image */
  486.                         windowPaintRect = paintRect;
  487.                         OffsetRect( &windowPaintRect, destRect.left,
  488.                                 destRect.top );
  489.  
  490.                         /* Only copy if some part of brush within image */
  491.                         if (SectRect( &windowPaintRect, &windowRect,
  492.                                 &windowPaintRect ))
  493.                         {
  494.                             /* Make clipped paint rect in off-screen image */
  495.                             paintRect = windowPaintRect;
  496.                             OffsetRect( &paintRect, -destRect.left,
  497.                                     -destRect.top );
  498.  
  499.                             /* Copy painted off-screen image to the screen */
  500.                             SetGWorld( (CGrafPtr)docWindow, nil );
  501.                             CopyBits( &((GrafPtr)image)->portBits, &docWindow->portBits,
  502.                                     &paintRect, &windowPaintRect,
  503.                                     srcCopy, nil );
  504.                         }
  505.  
  506.                         /* Remember the new mouse position */
  507.                         oldPosition = newPosition;
  508.                     }
  509.  
  510.                     /* Get the new position of the mouse */
  511.                     SetPort( docWindow );
  512.                     GetMouse( /*<*/&newPosition );
  513.                 }
  514.             }
  515.             break;
  516.         case inThumb:
  517.             /* Mouse in thumb of scroll bar; get existing control value */
  518.             scrollDelta = GetControlValue( scrollBar );
  519.  
  520.             /* Track mouse in thumb until mouse button is released */
  521.             partNumber = TrackControl( scrollBar, newPosition, nil );
  522.             if (partNumber != 0)
  523.             {
  524.                 /* Find diff between old and new scroll bar values */
  525.                 scrollDelta -= GetControlValue( scrollBar );
  526.                 if (scrollDelta != 0)
  527.                 {
  528.                     if (scrollBar == (**pictDocInfo).vScrollBar)
  529.                         ScrollPictDoc( docWindow, 0, scrollDelta );
  530.                     else
  531.                         ScrollPictDoc( docWindow, scrollDelta, 0 );
  532.                 }
  533.             }
  534.             break;
  535.         case inUpButton:
  536.         case inDownButton:
  537.         case inPageUp:
  538.         case inPageDown:
  539.             TrackControl( scrollBar, newPosition, gActionProc);
  540.             break;
  541.         default:
  542.             break;
  543.     }
  544. }
  545.  
  546.  
  547. /******************************************************************************\
  548. * NAME & SYNOPSIS:
  549. * ScrollActionProc: Handle a click in a scroll bar (except for thumb)
  550. *
  551. * PARAMETERS:
  552. * ControlHandle control: Control that received the mouse click
  553. * short         part:    Part number of the clicked control part
  554. *
  555. * DEFINITION:
  556. * ScrollActionProc is called by the Control Manager when TrackControl is called
  557. * in the ClickPictDoc function above.  It handles scrolling when the user clicks
  558. * in the scroll arrows or page areas of the scroll bar.  The document is
  559. * scrolled by the appropriate amount.
  560. *
  561. * DESCRIPTION:
  562. * ScrollActionProc first checks to see which part of the scroll bar was clicked.
  563. * If it was in either of the arrows, then the desired scroll amount is one
  564. * pixel.  If it was in either of the paging areas, then the desired scroll
  565. * amount is 32 pixels.  The current value of the clicked scroll bar is then
  566. * adjusted by the desired scroll amount, and the result is compared against the
  567. * limits of the scroll bar’s values.  If the new value is beyond either limit,
  568. * then it’s pinned back into the limits.  Then the real amount to scroll is
  569. * calculated simply by subtracting the new scroll value from the old scroll
  570. * value, and ScrollPictDoc (defined below) is called to scroll the Picture
  571. * Document window.
  572. *
  573. * RETURN VALUES:
  574. * None
  575. \******************************************************************************/
  576.  
  577. pascal  void ScrollActionProc(
  578.     ControlHandle control, /* Handle to clicked control */
  579.     short         part)    /* Part number of clicked control part */
  580. {
  581.     PictDocInfoHnd pictDocInfo; /* Handle to Picture Document info rec */
  582.     WindowPtr      docWindow;   /* Clicked Picture Document window */
  583.     Rect           windowRect;  /* Window rect except for scroll bars */
  584.     Rect           destRect;    /* Rectangle of image in window */
  585.     short          scrollDelta; /* Number of pixels to scroll by */
  586.     short          oldValue;    /* Value of scroll bar before scrolling */
  587.     short          newValue;    /* Value of scroll bar after scrolling */
  588.     short          maxValue;    /* Maximum value of the scroll bar */
  589.  
  590.     /* Get information about the Picture Document */
  591.     docWindow = (**control).contrlOwner;
  592.     pictDocInfo = (PictDocInfoHnd)GetWRefCon( docWindow );
  593.     windowRect = (**pictDocInfo).windowRect;
  594.     destRect = (**pictDocInfo).destRect;
  595.  
  596.     /* Only process if the part # isn’t zero */
  597.     if (part != 0)
  598.     {
  599.         /* Decide scroll amount based on part of control that was clicked */
  600.         switch (part)
  601.         {
  602.             case inUpButton:
  603.                 scrollDelta = 1;
  604.                 break;
  605.             case inDownButton:
  606.                 scrollDelta = -1;
  607.                 break;
  608.             case inPageUp:
  609.                 scrollDelta = 32;
  610.                 break;
  611.             case inPageDown:
  612.                 scrollDelta = -32;
  613.                 break;
  614.             default:
  615.                 scrollDelta = 0;
  616.                 break;
  617.         }
  618.  
  619.         /* Only do something if we’re actually scrolling */
  620.         if (scrollDelta != 0)
  621.         {
  622.             /* Get the current scroll bar state */
  623.             maxValue = GetControlMaximum( control );
  624.             oldValue = GetControlValue( control );
  625.  
  626.             /* Calculate the new scroll bar value pinned to the limits */
  627.             newValue = oldValue - scrollDelta;
  628.             if (newValue < 0)
  629.                 newValue = 0;
  630.             else if (newValue > maxValue)
  631.                 newValue = maxValue;
  632.  
  633.             /* Only scroll if the old and new values are different */
  634.             if (oldValue != newValue)
  635.             {
  636.                 /* Set the new value of the scroll bar */
  637.                 SetControlValue( control, newValue );
  638.  
  639.                 /* Scroll by difference between old and new scroll bar values */
  640.                 scrollDelta = oldValue - newValue;
  641.                 if (control == (**pictDocInfo).vScrollBar)
  642.                     ScrollPictDoc( docWindow, 0, scrollDelta );
  643.                 else
  644.                     ScrollPictDoc( docWindow, scrollDelta, 0 );
  645.             }
  646.         }
  647.     }
  648. }
  649.  
  650.  
  651. /******************************************************************************\
  652. * NAME & SYNOPSIS:
  653. * ScrollPictDoc: Scroll a Picture Document window’s image
  654. *
  655. * PARAMETERS:
  656. * WindowPtr docWindow:    Pointer to Picture Document window to scroll
  657. * short     vScrollDelta: Number of pixels to scroll vertically
  658. * short     hScrollDelta: Number of pixels to scroll horizontally
  659. *
  660. * DEFINITION:
  661. * ScrollPictDoc scrolls the picture document window specified by the docWindow
  662. * parameter by vScrollDelta pixels vertically and hScrollDelta pixels
  663. * horizontally.  The part of the window that’s scrolled in is updated, so no
  664. * updating is needed outside of ScrollPictDoc.
  665. *
  666. * DESCRIPTION:
  667. * ScrollRect is called to scroll the Picture Document’s image by the specified
  668. * amount.  The scrolled-in part is then redrawn with a call to CopyBits.  The
  669. * destRect field of the Picture Document information record keeps track of the
  670. * scrolled position of the image, so it’s updated to reflect the new scrolled
  671. * position.
  672. *
  673. * RETURN VALUES:
  674. * None
  675. \******************************************************************************/
  676.  
  677. static void ScrollPictDoc(
  678.     WindowPtr docWindow,    /* Pointer to document window to scroll */
  679.     short     hScrollDelta, /* Number of pixels to scroll horizontally */
  680.     short     vScrollDelta) /* Number of pixels to scroll vertically */
  681. {
  682.     PictDocInfoHnd pictDocInfo;  /* Handle to Picture Document info rec */
  683.     RgnHandle      updateRegion; /* Region covering scrolled-in area */
  684.     Rect           windowRect;   /* Rect of window except for scroll bars */
  685.     Rect           destRect;     /* New destination rectangle */
  686.     GWorldPtr      image;        /* Off-screen document image */
  687.  
  688.     /* Get the window rectangle and destination rectangle */
  689.     pictDocInfo = (PictDocInfoHnd)GetWRefCon( docWindow );
  690.     windowRect = (**pictDocInfo).windowRect;
  691.     destRect = (**pictDocInfo).destRect;
  692.     image = (**pictDocInfo).image;
  693.  
  694.     /* Scroll the image by the specified amount */
  695.     updateRegion = NewRgn();
  696.     ScrollRect( &windowRect, hScrollDelta, vScrollDelta, updateRegion );
  697.  
  698.     /* Update the destination rectangle to the new scrolled position */
  699.     OffsetRect( &destRect, hScrollDelta, vScrollDelta );
  700.     (**pictDocInfo).destRect = destRect;
  701.  
  702.     /* Update the scrolled-in part of the window */
  703.     CopyBits( &((GrafPtr)image)->portBits, &docWindow->portBits,
  704.             &image->portRect, &destRect,
  705.             srcCopy, updateRegion );
  706.  
  707.     /* Get rid of the update region */
  708.     DisposeRgn( updateRegion );
  709. }
  710.  
  711.  
  712. /******************************************************************************\
  713. * NAME & SYNOPSIS:
  714. * GrowPictDoc: Let the user change the size of a Picture Document window
  715. *
  716. * PARAMETERS:
  717. * See PictDocument.h
  718. *
  719. * DEFINITION:
  720. * See PictDocument.h
  721. *
  722. * DESCRIPTION:
  723. * GrowWindow is called with a boundary rectangle that specifies that the window
  724. * has a minimum size of kMinWindowSize (defined at the top of this source file)
  725. * and a maximum size that’s the size of the image.  After the user releases the
  726. * mouse button, SizeWindow is called to change the size of the window, and
  727. * ResizePictDoc (defined below) is called to update the Picture Document to the
  728. * new size.
  729. *
  730. * RETURN VALUES:
  731. * See PictDocument.h
  732. \******************************************************************************/
  733.  
  734. void GrowPictDoc(
  735.     WindowPtr   docWindow,   /* Picture Document window that’s to be grown */
  736.     EventRecord *clickEvent) /* Mouse down event */
  737. {
  738.     PictDocInfoHnd pictDocInfo; /* Handle to Picture Document info rec */
  739.     Rect           growBounds;  /* Window can be dragged over this rectangle */
  740.     Point          newSize;     /* New size of the new window */
  741.  
  742.     pictDocInfo = (PictDocInfoHnd)GetWRefCon( docWindow );
  743.  
  744.     growBounds = (**pictDocInfo).image->portRect;
  745.     growBounds.left = growBounds.top = kMinWindowSize;
  746.     growBounds.right += kScrollBarWidth;
  747.     growBounds.bottom += kScrollBarWidth;
  748.  
  749.     /* Let the user resize the window to any size */
  750.     *((long *)&newSize) = GrowWindow( docWindow, clickEvent->where,
  751.             &growBounds );
  752.  
  753.     /* If the user actually changed the size of the window, resize the window */
  754.     if (*((long *)&newSize) != 0)
  755.     {
  756.         SizeWindow( docWindow, newSize.h, newSize.v, true );
  757.         ResizePictDoc( docWindow );
  758.     }
  759. }
  760.  
  761.  
  762. /******************************************************************************\
  763. * NAME & SYNOPSIS:
  764. * ResizePictDoc: Update a picture document window to a new size
  765. *
  766. * PARAMETERS:
  767. * WindowPtr docWindow: Picture document window that was resized
  768. *
  769. * DEFINITION:
  770. * ResizePictDoc updates the Picture Document window specified by the docWindow
  771. * to a new size.  This routine is called right after the size of docWindow has
  772. * been changed.  The act of updating the Picture Document window involves moving
  773. * the scroll bars to the edge of the window and scrolling the document image.
  774. * The image has to be scrolled when the window is grown in case the bottom or
  775. * right edge of the window goes below or to the right of the image, in which
  776. * case the bottom or right edges of the image has to “chase” the bottom or right
  777. * edges of the window, and that means scrolling.
  778. *
  779. * DESCRIPTION:
  780. * The first major thing that’s done is a new destination rectangle is calculated
  781. * in case the document has to be scrolled as described in the definition.  There
  782. * are three conditions that have to be met before scrolling can happen:
  783. *     1) The upper-left corner of destRect must not be (0,0), meaning that the
  784. *        document’s image is scrolled from its original position.
  785. *     2) The window has become bigger.
  786. *     3) The window’s rectangle has moved below or to the right of the image in
  787. *        the window.
  788. * If all three of these conditions are met for either the horizontal or
  789. * vertical cases, a new destRect is calculated.  If this new destRect is
  790. * different from the old destRect, ScrollPictDoc (defined above) is called to
  791. * scroll the image to the new position.
  792. *
  793. * After this, the windowRect field of the Picture Document information record
  794. * is updated to the new size of the window.  Then, the maximum values of the
  795. * scroll bars are set to the number of pixels in the horizontal or vertical
  796. * direction that can’t be seen.  And then, the old position of the scroll icon
  797. * is invalidated so that it’ll be erased with the document’s image the next time
  798. * the window is updated.
  799. *
  800. * Both of the scroll bars are then moved along the bottom and right edges of the
  801. * window, and the new position of the grow icon is invalidated so that it’s
  802. * redrawn in case the window was made smaller.  Finally, the rectangles of the
  803. * scroll bars are validated so that they won’t be redrawn in the next update
  804. * event.
  805. *
  806. * RETURN VALUES:
  807. * None
  808. \******************************************************************************/
  809.  
  810. static void ResizePictDoc(
  811.     WindowPtr docWindow) /* Picture document window that was resized */
  812. {
  813.     PictDocInfoHnd pictDocInfo;   /* Handle to Picture Document information rec */
  814.     ControlHandle  vScrollBar;    /* Vertical scroll bar */
  815.     ControlHandle  hScrollBar;    /* Horizontal scroll bar */
  816.     Rect           windowRect;    /* Covers part of window that contains image */
  817.     Rect           newWindowRect; /* Covers all of window except scroll bars */
  818.     Rect           scrollRect;    /* Rectangle of scroll bar */
  819.     Rect           destRect;      /* Rectangle of to draw image into */
  820.     Rect           newDestRect;   /* Rectangle of to draw image into */
  821.     CGrafPtr       image;         /* Off-screen document image */
  822.     short          remaining;     /* # rows, columns of pixels not visible */
  823.  
  824.     SetPort( docWindow );
  825.  
  826.     /* Get the Picture Document’s information */
  827.     pictDocInfo = (PictDocInfoHnd)GetWRefCon( docWindow );
  828.     vScrollBar = (**pictDocInfo).vScrollBar;
  829.     hScrollBar = (**pictDocInfo).hScrollBar;
  830.     image = (**pictDocInfo).image;
  831.     destRect = (**pictDocInfo).destRect;
  832.     windowRect = (**pictDocInfo).windowRect;
  833.  
  834.     /* Find the new window document area */
  835.     newWindowRect = docWindow->portRect;
  836.     newWindowRect.right -= kScrollBarWidth - 1;
  837.     newWindowRect.bottom -= kScrollBarWidth - 1;
  838.  
  839.     /* Find new destRect in case window growing causes scrolling */
  840.     newDestRect = destRect;
  841.     if (newDestRect.left != 0 && 
  842.             (newWindowRect.right - newWindowRect.left) >
  843.             (windowRect.right - windowRect.left) &&
  844.             newWindowRect.right > newDestRect.right)
  845.         OffsetRect( /*◊*/&newDestRect, (newWindowRect.right - windowRect.right)
  846.                 - (newDestRect.right - windowRect.right), 0 );
  847.     if (newDestRect.top != 0 && 
  848.             (newWindowRect.bottom - newWindowRect.top) >
  849.             (windowRect.bottom - windowRect.top) &&
  850.             newWindowRect.bottom > newDestRect.bottom)
  851.         OffsetRect( /*◊*/&newDestRect, 0, (newWindowRect.bottom -
  852.                 windowRect.bottom) - (newDestRect.bottom - windowRect.bottom) );
  853.  
  854.     /* If the new scrolled position has changed, update the window’s image */
  855.     if (!EqualRect( &newDestRect, &destRect))
  856.         ScrollPictDoc( docWindow, newDestRect.left - destRect.left,
  857.                 newDestRect.top - destRect.top );
  858.  
  859.     /* Set up the rectangles for scroll bar calculations */
  860.     (**pictDocInfo).windowRect = newWindowRect;
  861.  
  862.     /* Calc # rows of image not visible; set as max value of scroll bar */
  863.     remaining = image->portRect.bottom - newWindowRect.bottom;
  864.     if (remaining < 0)
  865.         remaining = 0;
  866.     SetControlMaximum( vScrollBar, remaining );
  867.  
  868.     /* Calc # cols of image not visible; set as max value of scroll bar */
  869.     remaining = image->portRect.right - newWindowRect.right;
  870.     if (remaining < 0)
  871.         remaining = 0;
  872.     SetControlMaximum( hScrollBar, remaining );
  873.  
  874.     /* Invalidate the old position of the grow icon */
  875.     SetRect( &scrollRect, (**hScrollBar).contrlRect.right,
  876.             (**vScrollBar).contrlRect.bottom,
  877.             (**vScrollBar).contrlRect.right,
  878.             (**hScrollBar).contrlRect.bottom );
  879.     InvalRect( &scrollRect );
  880.  
  881.     /* Move the scroll bars along the edges of the window */
  882.     HideControl( vScrollBar );
  883.     HideControl( hScrollBar );
  884.     MoveControl( vScrollBar, newWindowRect.right, newWindowRect.top - 1 );
  885.     MoveControl( hScrollBar, newWindowRect.left - 1, newWindowRect.bottom );
  886.     SizeControl( vScrollBar, kScrollBarWidth, newWindowRect.bottom -
  887.             newWindowRect.top + 2 );
  888.     SizeControl( hScrollBar, newWindowRect.right - newWindowRect.left + 2,
  889.             kScrollBarWidth );
  890.     ShowControl( vScrollBar );
  891.     ShowControl( hScrollBar );
  892.  
  893.     /* Invalidate the new position of the grow icon */
  894.     SetRect( &scrollRect, (**hScrollBar).contrlRect.right,
  895.             (**vScrollBar).contrlRect.bottom,
  896.             (**vScrollBar).contrlRect.right,
  897.             (**hScrollBar).contrlRect.bottom );
  898.     InvalRect( &scrollRect );
  899.  
  900.     /* Validate the new scroll bar areas */
  901.     scrollRect = (**vScrollBar).contrlRect;
  902.     ValidRect( &scrollRect );
  903.     scrollRect = (**hScrollBar).contrlRect;
  904.     ValidRect( &scrollRect );
  905. }
  906.  
  907.  
  908. /******************************************************************************\
  909. * NAME & SYNOPSIS:
  910. * ActivatePictDoc: Activate or deactivate a Picture Document window
  911. *
  912. * PARAMETERS:
  913. * See PictDocument.h
  914. *
  915. * DEFINITION:
  916. * See PictDocument.h
  917. *
  918. * DESCRIPTION:
  919. * If the Picture Document window is becoming active, the scroll bars are shown.
  920. * If it’s becoming inactive, the scroll bars are hidden.  Because hiding
  921. * controls causes the control rectangle to be invalidated, ActivatePictDoc
  922. * validates them again to avoid a very slight flicker in the lines that delimit
  923. * the scroll bar areas.
  924. *
  925. * DrawGrowIcon is called to show or hide the grow icon.  The Window Manager
  926. * automatically decides whether to show or hide the grow icon.
  927. *
  928. * RETURN VALUES:
  929. * See PictDocument.h
  930. \******************************************************************************/
  931.  
  932. void ActivatePictDoc(
  933.     WindowPtr docWindow,      /* Pointer to Doc window being updated */
  934.     Boolean   becomingActive) /* True if Doc wind is becoming active */
  935. {
  936.     PictDocInfoHnd pictDocInfo; /* Handle to Picture Document information rec */
  937.     ControlHandle  vScrollBar;  /* Vertical scroll bar */
  938.     ControlHandle  hScrollBar;  /* Horizontal scroll bar */
  939.     Rect           scrollRect;  /* Rectangle of the scroll bars */
  940.  
  941.     /* Get the scroll bars of the window */
  942.     pictDocInfo = (PictDocInfoHnd)GetWRefCon( docWindow );
  943.     vScrollBar = (**pictDocInfo).vScrollBar;
  944.     hScrollBar = (**pictDocInfo).hScrollBar;
  945.  
  946.     /* Show scroll bars if becoming active; hide if becoming inactive */
  947.     if (becomingActive)
  948.     {
  949.         ShowControl( vScrollBar );
  950.         ShowControl( hScrollBar );
  951.     }
  952.     else
  953.     {
  954.         HideControl( vScrollBar );
  955.         HideControl( hScrollBar );
  956.  
  957.         /* To avoid a slight flicker, validate the scroll bar rectangles */
  958.         SetPort( docWindow );
  959.         scrollRect = (**vScrollBar).contrlRect;
  960.         ValidRect( &scrollRect );
  961.         scrollRect = (**hScrollBar).contrlRect;
  962.         ValidRect( &scrollRect );
  963.     }
  964.  
  965.     /* Show or hide the grow icon */
  966.     DrawGrowIcon( docWindow );
  967. }
  968.  
  969.  
  970. /******************************************************************************\
  971. * NAME & SYNOPSIS:
  972. * FixPictDocMenus: Dim, undim, check, or uncheck menus for Picture Documents
  973. *
  974. * PARAMETERS:
  975. * See PictDocument.h
  976. *
  977. * DEFINITION:
  978. * See PictDocument.h
  979. *
  980. * DESCRIPTION:
  981. * The Close and Save As… items in the File menu are enabled when a Picture
  982. * Document window is active, as is the Choose Color… item in the Colors menu.
  983. *
  984. * RETURN VALUES:
  985. * See PictDocument.h
  986. \******************************************************************************/
  987.  
  988. void FixPictDocMenus(
  989.     WindowPtr docWindow) /* Pointer to active Picture Document window */
  990. {
  991. #pragma unused(docWindow)
  992.     MenuHandle aMenu; /* Handle to the menus being udpated */
  993.  
  994.     /* Set items in the File menu */
  995.     aMenu = GetMenuHandle( mFile );
  996.     EnableItem( aMenu, iClose );
  997.     EnableItem( aMenu, iSaveAs );
  998. }
  999.  
  1000.  
  1001. /******************************************************************************\
  1002. * NAME & SYNOPSIS:
  1003. * DoOpenPictDoc: Open a new Picture Document
  1004. *
  1005. * PARAMETERS:
  1006. * See PictDocument.h
  1007. *
  1008. * DEFINITION:
  1009. * See PictDocument.h
  1010. *
  1011. * DESCRIPTION:
  1012. * The usual Standard File dialog is used to get the name and location of the
  1013. * PICT file to open.  Then FindPictDoc (defined earlier in this file) is called
  1014. * to determine whether the chosen PICT file is already open within this
  1015. * application.  If it is, then that PICT file’s window is just selected and
  1016. * DoOpenPictDoc returns with a pointer to that window.
  1017. *
  1018. * If the file isn’t open, then it’s opened here with HOpenDF.  This routine is
  1019. * used because it works even on systems that don’t have the FSSpec-based File
  1020. * Manager calls.  GetPictureRect (defined later in this file) is called to get
  1021. * the picFrame from the picture in the chosen PICT file.  Once this is done,
  1022. * CreatePictDoc is called to create a blank Picture Document window that’s ready
  1023. * to read the image into.
  1024. *
  1025. * The current port is set to the off-screen GrafPort that CreatePictDoc created,
  1026. * and then DrawFilePicture (defined later in this file) is called to spool the
  1027. * PICT file into the current port.
  1028. *
  1029. * RETURN VALUES:
  1030. * See PictDocument.h
  1031. \******************************************************************************/
  1032.  
  1033. WindowPtr DoOpenPictDoc()
  1034. {
  1035.     WindowPtr         pictDocWindow;  /* Pointer to new Picture Doc window */
  1036.     PictDocInfoHnd    pictDocInfo;    /* Handle to Picture Document info rec */
  1037.     PictureInfoRec    pictureInfo;    /* PixMap information from PICT file */
  1038.     Rect              pictureRect;    /* Rectangle of picture in PICT file */
  1039.     CGrafPtr          newImage;       /* New off-screen image */
  1040.     RGBColor          color;          /* Use to set default fore/back colors */
  1041.     CGrafPtr          savedPort;      /* Ptr to current port for restoring */
  1042.     GDHandle          savedDevice;    /* Current GDevice for restoring */
  1043.     StandardFileReply reply;          /* User’s choice for PICT file to open */
  1044.     SFTypeList        typeList;       /* Specifies we can open 'PICT' files */
  1045.     short             pictFileRef;    /* File reference number of PICT file */
  1046.     OSErr             error;
  1047.     ExceptionRec      exception;
  1048.     long              msgType;
  1049.     long              msgCode;
  1050.  
  1051.     pictDocWindow = nil;
  1052.     pictFileRef = 0;
  1053.  
  1054.     /* Exception-handling section */
  1055.     if (ExceptionEntry( /*<*/&exception, /*<*/&msgType, /*<*/&msgCode ))
  1056.     {
  1057.         if (pictFileRef != 0)
  1058.             (void)FSClose( pictFileRef );
  1059.         if (pictDocWindow != nil)
  1060.             DoClosePictDoc( pictDocWindow );
  1061.         (void)ShowAlert( kStopAlert, rOKAlertID, msgType, msgCode );
  1062.         return nil;
  1063.     }
  1064.  
  1065.     /* Let the user choose a PICT file */
  1066.     typeList [0] = 'PICT';
  1067.     if (FileSpecGet( nil, 1, typeList, /*<*/&reply ))
  1068.     {
  1069.         /* First determine whether chosen file is already open in this app */
  1070.         pictDocWindow = FindPictDoc( &reply.sfFile );
  1071.         if (pictDocWindow == nil)
  1072.         {
  1073.             /* Open the PICT file */
  1074.             error = HOpenDF( reply.sfFile.vRefNum, reply.sfFile.parID,
  1075.                     reply.sfFile.name, fsCurPerm, /*<*/&pictFileRef );
  1076.             if (error != noErr)
  1077.                 if (error == opWrErr)
  1078.                     Exception( &exception, rFileErrMessages,
  1079.                             kFileErrDocOpenMsg );
  1080.                 else
  1081.                     Exception( &exception, rMiscErrMessages,
  1082.                             kMiscErrUnknownMsg );
  1083.  
  1084.             /* Get interesting information about a picture */
  1085.             error = GetPictureInfo( pictFileRef, /*<*/&pictureInfo );
  1086.             if (error != noErr)
  1087.                 Exception( &exception, rMiscErrMessages, kMiscErrUnknownMsg );
  1088.             pictureRect = pictureInfo.frame;
  1089.             OffsetRect( /*◊*/&pictureRect, -pictureRect.left,
  1090.                     -pictureRect.top );
  1091.  
  1092.             /* Open a new Picture Document window*/
  1093.             error = CreatePictDoc( &pictureRect, pictureInfo.depth,
  1094.                     pictureInfo.colors, /*<*/&pictDocWindow );
  1095.             if (error == memFullErr)
  1096.                 Exception( &exception, rMemErrMessages, kMemErrOpenDocMsg );
  1097.             else if (error == resNotFound)
  1098.                 Exception( &exception, rResErrMessages, kResErrAppDamageMsg );
  1099.             else if (error != noErr)
  1100.                 Exception( &exception, rMiscErrMessages, kMiscErrUnknownMsg );
  1101.  
  1102.             /* Fill in the Picture Document information */
  1103.             pictDocInfo = (PictDocInfoHnd)GetWRefCon( pictDocWindow );
  1104.             color.red = color.green = color.blue = 0x0000;
  1105.             (**pictDocInfo).foreground = color;
  1106.             color.red = color.green = color.blue = 0xFFFF;
  1107.             (**pictDocInfo).background = color;
  1108.             (**pictDocInfo).fileRef = pictFileRef;
  1109.             (**pictDocInfo).file = reply.sfFile;
  1110.             (**pictDocInfo).dithering = false;
  1111.  
  1112.             /* Get a handle to the Picture Document information record */
  1113.             pictDocInfo = (PictDocInfoHnd)GetWRefCon( pictDocWindow );
  1114.  
  1115.             /* Save current port and set newImage as current port */
  1116.             newImage = (**pictDocInfo).image;
  1117.             GetGWorld( /*<*/&savedPort, /*<*/&savedDevice );
  1118.             SetGWorld( newImage, nil );
  1119.  
  1120.             /* Draw the contents of the PICT file into the current port */
  1121.             error = DrawFilePicture( pictFileRef, &newImage->portRect );
  1122.  
  1123.             /* Restore the current port and check for errors */
  1124.             SetGWorld( savedPort, savedDevice );
  1125.             if (error != noErr)
  1126.                 Exception( &exception, rMiscErrMessages, kMiscErrUnknownMsg );
  1127.  
  1128.             /* Set the title of the window to the title of the file */
  1129.             SetWTitle( pictDocWindow, reply.sfFile.name );
  1130.  
  1131.             /* Show the window in its final size and position */
  1132.             ShowWindow( pictDocWindow );    
  1133.         }
  1134.         else
  1135.             /* Chosen file already open; just select it */
  1136.             SelectWindow( pictDocWindow );
  1137.     }
  1138.  
  1139.     return pictDocWindow;
  1140. }
  1141.  
  1142.  
  1143. /******************************************************************************\
  1144. * NAME & SYNOPSIS:
  1145. * CreatePictDoc: Create an empty Picture Document
  1146. *
  1147. * PARAMETERS:
  1148. * Rect      *pictureRect: Bounding rectangle of the image
  1149. * WindowPtr *retDoc:      Returns pointer to new Picture Document window
  1150. *
  1151. * DEFINITION:
  1152. * This routine creates a new Picture Document window and returns a pointer to
  1153. * it.  If the Picture Document couldn’t be created for some reason, then
  1154. * CreatePictDoc returns the error code, and the retDoc parameter is untouched.
  1155. *
  1156. * DESCRIPTION:
  1157. * Space for the window record is allocated before GetNewWindow is called so that]
  1158. * heap fragmentation is minimized.  Then, the window is initialized with
  1159. * GetNewWindow.  Then the vertical and horizontal scroll bars are created, and
  1160. * the Picture Document information record is allocated and initialized.  The
  1161. * last major task is to stagger the window into position, and the routines that
  1162. * are defined in WindowPositioner.c are used to do this.
  1163. *
  1164. * RETURN VALUES:
  1165. * Result: Error code if Picture Document window couldn’t be created
  1166. * retDoc: Pointer to the new Picture Document window, 
  1167. \******************************************************************************/
  1168.  
  1169. static OSErr CreatePictDoc(
  1170.     Rect       *pictureRect, /* Bounding rectangle of off-screen image */
  1171.     short      pictureDepth, /* Pixel depth of off-screen image */
  1172.     CTabHandle pictureClut,  /* Color table of off-screen image */
  1173.     WindowPtr  *retDoc)      /* Returns ptr to new Picture Document window */
  1174. {
  1175.     WindowPtr      docWindow;      /* Pointer to new Picture Document window */
  1176.     Ptr            windowStore;    /* Pointer to pre-allocated window rec */
  1177.     PictDocInfoHnd docInfo;        /* Handle to Picture Document information */
  1178.     PaletteHandle  pictureColors;  /* Palette with picture’s colors */
  1179.     GWorldPtr      newImage;       /* New off-screen image */
  1180.     ControlHandle  vScrollBar;     /* Vertical scroll bar */
  1181.     ControlHandle  hScrollBar;     /* Horizontal scroll bar */
  1182.     Rect           windowRect;     /* Destination rectangle of window */
  1183.     Point          windowBias;     /* Bias of Picture Document window */
  1184.     OSErr          error;
  1185.     ExceptionRec   exception;
  1186.     long           msgType;
  1187.     long           msgCode;
  1188.     Point          tempPt;
  1189.  
  1190.     docWindow = nil;
  1191.     docInfo = nil;
  1192.     windowStore = nil;
  1193.  
  1194.     /* Exception-handling code */
  1195.     if (ExceptionEntry( /*<*/&exception, /*<*/&msgType, /*<*/&msgCode ))
  1196.     {
  1197.         if (docWindow != nil)
  1198.             CloseWindow( docWindow );
  1199.         if (windowStore != nil)
  1200.             DisposePtr( windowStore );
  1201.         if (docInfo != nil)
  1202.             DisposeHandle( (Handle)docInfo );
  1203.         return msgCode;
  1204.     }
  1205.  
  1206.     /* Pre-allocate the window record to avoid heap fragmentation */
  1207.     windowStore = NewPtrMargin( sizeof (WindowRecord), kAllocApp, !kAllocClr );
  1208.     if (windowStore == nil)
  1209.         Exception( &exception, 0, memFullErr );
  1210.  
  1211.     /* Create the off-screen graphics environment */
  1212.     if (pictureClut == nil)
  1213.         pictureClut = GetCTable( pictureDepth );
  1214.     error = NewGWorld( /*<*/&newImage, pictureDepth, pictureRect, pictureClut,
  1215.             nil, 0 );
  1216.     if (error != noErr)
  1217.         Exception( &exception, 0, error );
  1218.  
  1219.     /* Create a new Picture Document window */
  1220.     docWindow = GetNewCWindow( rPictDocWindID, windowStore,
  1221.             (WindowPtr)-1L );
  1222.     if (docWindow == nil)
  1223.         if (FailLowMemory( 0 ) || ResError() == memFullErr)
  1224.             Exception( &exception, 0, memFullErr );
  1225.         else if (ResError() == noErr || ResError() == resNotFound)
  1226.             Exception( &exception, 0, resNotFound );
  1227.     SetPort( docWindow );
  1228.  
  1229.     /* Give the window a palette with the window colors */
  1230.     pictureColors = NewPalette( (**pictureClut).ctSize + 1, pictureClut,
  1231.             pmTolerant, 0 );
  1232.     SetPalette( docWindow, pictureColors, true );
  1233.  
  1234.     /* Create the horizontal and vertical scroll bars */
  1235.     vScrollBar = GetNewControl( rScrollBarID, docWindow );
  1236.     if (vScrollBar == nil)
  1237.         if (FailLowMemory( 0 ) || ResError() == memFullErr)
  1238.             Exception( &exception, 0, memFullErr );
  1239.         else if (ResError() == noErr || ResError() == resNotFound)
  1240.             Exception( &exception, 0, resNotFound );
  1241.     hScrollBar = GetNewControl( rScrollBarID, docWindow );
  1242.     if (hScrollBar == nil)
  1243.         if (FailLowMemory( 0 ) || ResError() == memFullErr)
  1244.             Exception( &exception, 0, memFullErr );
  1245.         else if (ResError() == noErr || ResError() == resNotFound)
  1246.             Exception( &exception, 0, resNotFound );
  1247.  
  1248.     /* Allocate a new Picture Document information record */
  1249.     docInfo = (PictDocInfoHnd)NewHandleMargin( sizeof (PictDocInfoRec),
  1250.             kAllocApp, !kAllocClr );
  1251.     if (docInfo == nil)
  1252.         Exception( &exception, rMemErrMessages, kMemErrOpenDocMsg );
  1253.     (**docInfo).image = newImage;
  1254.     (**docInfo).vScrollBar = vScrollBar;
  1255.     (**docInfo).hScrollBar = hScrollBar;
  1256.     (**docInfo).destRect = newImage->portRect;
  1257.     (**docInfo).fileRef = 0;
  1258.     SetWRefCon( docWindow, (long)docInfo );
  1259.  
  1260.     /* Stagger the window onto the screen */
  1261.     windowBias = CalcWindowBias( documentProc,
  1262.             ((WindowPeek)docWindow)->goAwayFlag );
  1263.     SetRect( &windowRect, 0, 0, pictureRect->right + kScrollBarWidth - 1,
  1264.             pictureRect->bottom + kScrollBarWidth - 1 );
  1265.             
  1266.     /*convert the windowRect to global coordinates */
  1267.     SetPt(&tempPt, windowRect.left, windowRect.top);
  1268.     LocalToGlobal(&tempPt);
  1269.     windowRect.left = tempPt.h;
  1270.     windowRect.top = tempPt.v;
  1271.     SetPt(&tempPt, windowRect.right, windowRect.bottom);
  1272.     LocalToGlobal(&tempPt);
  1273.     windowRect.right = tempPt.h;
  1274.     windowRect.bottom = tempPt.v;
  1275.  
  1276.     PositionScreenRect( &windowRect,
  1277.             kParentScreenPos, kStaggerPos, NextPictDocWindow( nil ),
  1278.             windowBias.h, windowBias.v );
  1279.     MoveWindow( docWindow, windowRect.left, windowRect.top, false );
  1280.     SizeWindow( docWindow, windowRect.right - windowRect.left,
  1281.             windowRect.bottom - windowRect.top, false );
  1282.     ResizePictDoc( docWindow );
  1283.  
  1284.     /* Identify this window for later */
  1285.     ((WindowPeek)docWindow)->windowKind = kDocWKind;
  1286.  
  1287.     *retDoc = docWindow;
  1288.     return noErr;
  1289. }
  1290.  
  1291.  
  1292. /******************************************************************************\
  1293. * NAME & SYNOPSIS:
  1294. * DoSaveAsPictDoc: Save a new Picture Document
  1295. *
  1296. * PARAMETERS:
  1297. * See PictDocument.h
  1298. *
  1299. * DEFINITION:
  1300. * See PictDocument.h
  1301. *
  1302. * DESCRIPTION:
  1303. * The user is asked for a file name for the new Picture Document file through
  1304. * Standard File.  If the user specifies the file name and location and chooses
  1305. * OK, DoSaveAsPictDoc creates a new PICT file if it didn’t exist already, and
  1306. * then it opens the PICT file for writing.  The Picture Document’s off-screen
  1307. * GrafPort is set as the current port, and then OpenFilePicture (defined below)
  1308. * is called which opens a new picture for spooling.  Then the off-screen image
  1309. * is CopyBits’ed on top of itself which spools the off-screen image to the PICT
  1310. * file.  Then the spooled picture is closed, which completes the writing of the
  1311. * PICT file.
  1312. *
  1313. * RETURN VALUES:
  1314. * See PictDocument.h
  1315. \******************************************************************************/
  1316.  
  1317. void DoSaveAsPictDoc(
  1318.     WindowPtr docWindow) /* Pointer to Picture Document window being saved */
  1319. {
  1320.     StandardFileReply reply;       /* Location and name of new PICT file */
  1321.     Str255            docTitle;    /* Holds the existing name of the document */
  1322.     PictDocInfoHnd    docInfo;     /* Handle to Picture Document information */
  1323.     CGrafPtr          image;       /* Points to document’s image */
  1324.     CGrafPtr          savedPort;   /* Saves current port for later restoring */
  1325.     GDHandle          savedDevice; /* Saves current GDevice for restoring */
  1326.     short             pictFileRef; /* File reference number of PICT file */
  1327.     OSErr             error;
  1328.     ExceptionRec      exception;
  1329.     long              msgType;
  1330.     long              msgCode;
  1331.  
  1332.     pictFileRef = 0;
  1333.  
  1334.     /* Exception-handling code */
  1335.     if (ExceptionEntry( /*<*/&exception, /*<*/&msgType, /*<*/&msgCode ))
  1336.     {
  1337.         if (pictFileRef != 0)
  1338.             (void)FSClose( pictFileRef );
  1339.         return;
  1340.     }
  1341.  
  1342.     /* Get the name and location of the new file from the user */
  1343.     GetWTitle( docWindow, /*<*/docTitle );
  1344.     StandardPutFile( "\P", docTitle, &reply );
  1345.     if (reply.sfGood)
  1346.     {
  1347.         /* Create the new PICT file if it doesn’t already exist */
  1348.         if (!reply.sfReplacing)
  1349.         {
  1350.             error = HCreate( reply.sfFile.vRefNum, reply.sfFile.parID,
  1351.                     reply.sfFile.name, kFileCreator, kFileType );
  1352.             if (error != noErr)
  1353.                 Exception( &exception, rMiscErrMessages, kMiscErrUnknownMsg );
  1354.         }
  1355.  
  1356.         /* Open the new PICT file */
  1357.         error = HOpen( reply.sfFile.vRefNum, reply.sfFile.parID,
  1358.                 reply.sfFile.name, fsCurPerm, /*<*/&pictFileRef );
  1359.         if (error != noErr)
  1360.             Exception( &exception, rMiscErrMessages, kMiscErrUnknownMsg );
  1361.  
  1362.         /* Get the Picture Document’s image and make it the current port */
  1363.         docInfo = (PictDocInfoHnd)GetWRefCon( docWindow );
  1364.         image = (**docInfo).image;
  1365.         GetGWorld( /*<*/&savedPort, /*<*/&savedDevice );
  1366.         SetGWorld( image, nil );
  1367.  
  1368.         /* Create a PICT file with the Picture Document’s image */
  1369.         error = OpenFilePicture( pictFileRef, &image->portRect );
  1370.         if (error != noErr)
  1371.             Exception( &exception, rMiscErrMessages, kMiscErrUnknownMsg );
  1372.         CopyBits( &((GrafPtr)image)->portBits, &((GrafPtr)image)->portBits,
  1373.                 &image->portRect, &image->portRect,
  1374.                 srcCopy, nil );
  1375.         error = CloseFilePicture();
  1376.         if (error != noErr)
  1377.             Exception( &exception, rMiscErrMessages, kMiscErrUnknownMsg );
  1378.  
  1379.         /* Restore the current port */
  1380.         SetGWorld( savedPort, savedDevice );
  1381.  
  1382.         /* Close the existing PICT file if any */
  1383.         if ((**docInfo).fileRef != 0)
  1384.             (void)FSClose( (**docInfo).fileRef );
  1385.  
  1386.         /* Remember the new PICT file’s reference number */
  1387.         (**docInfo).fileRef = pictFileRef;
  1388.         (**docInfo).file = reply.sfFile;
  1389.  
  1390.         /* Set the title of the window to the title of the file */
  1391.         SetWTitle( docWindow, reply.sfFile.name );
  1392.     }
  1393. }
  1394.  
  1395.  
  1396. /******************************************************************************\
  1397. * NAME & SYNOPSIS:
  1398. * DoClosePictDoc: Close a Picture Document window
  1399. *
  1400. * PARAMETERS:
  1401. * See PictDocument.h
  1402. *
  1403. * DEFINITION:
  1404. * See PictDocument.h
  1405. *
  1406. * DESCRIPTION:
  1407. * The off-screen image is disposed of and the Picture Document’s PICT file is
  1408. * closed.  Then all the memory associated with the window is disposed of.
  1409. *
  1410. * RETURN VALUES:
  1411. * See PictDocument.h
  1412. \******************************************************************************/
  1413.  
  1414. Boolean DoClosePictDoc(
  1415.     WindowPtr docWindow) /* Ptr to Picture Document window being closed */
  1416. {
  1417.     PictDocInfoHnd docInfo; /* Handle to Picture Document info record */
  1418.  
  1419.     docInfo = (PictDocInfoHnd)GetWRefCon( docWindow );
  1420.     if (docInfo != nil)
  1421.     {
  1422.         /* Get rid of the off-screen CGrafPort */
  1423.         if ((**docInfo).image != nil)
  1424.             DisposeGWorld( (**docInfo).image );
  1425.  
  1426.         /* Close the Picture File */
  1427.         if ((**docInfo).fileRef != 0)
  1428.             (void)FSClose( (**docInfo).fileRef );
  1429.  
  1430.         /* Dispose of the Picture Document information record */
  1431.         DisposeHandle( (Handle)docInfo );
  1432.     }
  1433.  
  1434.     /* Close and dispose of the Picture Document window */
  1435.     DisposePalette( GetPalette( docWindow ) );
  1436.     CloseWindow( docWindow );
  1437.     DisposePtr( (Ptr)docWindow );
  1438.  
  1439.     return true;
  1440. }
  1441.  
  1442.  
  1443. /******************************************************************************\
  1444. * NAME & SYNOPSIS:
  1445. * GetPictureInfo: Get basic information about a PICT document
  1446. *
  1447. * PARAMETERS:
  1448. * short          pictFileRef:  File reference number of PICT file
  1449. * PictureInfoRec *retPictRect: Returns information on picture
  1450. *
  1451. * DEFINITION:
  1452. * GetPictureInfo examines the PICT file whose file reference number is specified
  1453. * in the pictFileRef parameter to get the picFrame of the picture, and the pixel
  1454. * depth and color table of the deepest PixMap in the picture.  This information
  1455. * is returned in the retPictInfo parameter.
  1456. *
  1457. * DESCRIPTION:
  1458. * GetPictureInfo uses picture spooling to get the information about the picture
  1459. * in the specified PICT file.  A temporary CGrafPort or GrafPort is set up just
  1460. * so that no existing ports are touched.  Then a CQDProcs or QDProcs record is
  1461. * set up so that picture data comes from the PICT file instead of memory, and so
  1462. * that any CopyBits calls within the picture get routed to the InfoBits routine
  1463. * (defined below).  Then the standard picture spooling mechanism is used to read
  1464. * the PICT file.  The clip region is set to nothing to prevent drawing——only the
  1465. * information about the PixMaps is needed, and InfoBits gets that.
  1466. *
  1467. * RETURN VALUES:
  1468. * Result:      Error code if anything went wrong
  1469. * retPictInfo: Size of picture, and depth and color table of deepest PixMap in
  1470. *              the picture.
  1471. \******************************************************************************/
  1472.  
  1473. static OSErr GetPictureInfo(
  1474.     short          pictFileRef,  /* File reference number of PICT file */
  1475.     PictureInfoRec *retPictInfo) /* Returns information on picture */
  1476. {
  1477.     CGrafPort    tempPort;     /* Temp port used for “Drawing” pict */
  1478.     Boolean      tempPortOpen; /* True if tempPort has been opened */
  1479.     GrafPtr      savedPort;    /* Saves current port for restoring */
  1480.     CQDProcs     customProcs;  /* Custom GrafProcs */
  1481.     PicHandle    spoolPict;    /* Picture used for spooling */
  1482.     Rect         emptyRect;    /* Used to set empty clipping region */
  1483.     long         dataLen;      /* Number of bytes of data to read */
  1484.     long         qdVersion;    /* Version number of QuickDraw */
  1485.     OSErr        error;
  1486.     ExceptionRec exception;
  1487.     long         errorCode;
  1488.  
  1489.     error = noErr;
  1490.     tempPortOpen = false;
  1491.     savedPort = nil;
  1492.     spoolPict = nil;
  1493.  
  1494.     /* Exception-handling code */
  1495.     if (ExceptionEntry( /*<*/&exception, nil, /*<*/&errorCode ))
  1496.     {
  1497.         SetPort( savedPort );
  1498.         if (tempPortOpen)
  1499.             ClosePort( (GrafPtr)&tempPort );
  1500.         if (spoolPict != nil)
  1501.             DisposeHandle( (Handle)spoolPict );
  1502.         return errorCode;
  1503.     }
  1504.  
  1505.     /* Get the current version of QuickDraw */
  1506.     (void)Gestalt( gestaltQuickdrawVersion, /*<*/&qdVersion );
  1507.  
  1508.     /* Save current port so that we can restore it later */
  1509.     GetPort( /*<*/&savedPort );
  1510.  
  1511.     /* Open the temporary CGrafPort or GrafPort, depending on QD version */
  1512.     if (qdVersion >= gestalt8BitQD)
  1513.         OpenCPort( &tempPort );
  1514.     else
  1515.         OpenPort( (GrafPtr)&tempPort );
  1516.     tempPortOpen = true;
  1517.  
  1518.     /* Don’t want anything drawn */
  1519.     SetRect( /*<*/&emptyRect, 0, 0, 0, 0 );
  1520.     ClipRect( &emptyRect );
  1521.  
  1522.     /* Set up the GrafProcs for spooling */
  1523.     if (qdVersion >= gestalt8BitQD)
  1524.         SetStdCProcs( /*<*/&customProcs );
  1525.     else
  1526.         SetStdProcs( /*<*/(QDProcsPtr) &customProcs );
  1527.     customProcs.bitsProc = gQDBitsUPP;
  1528.     customProcs.getPicProc = gQDGetPicUPP;
  1529.  
  1530.     /* Install the custom GrafProcs into the tempPort */
  1531.     tempPort.grafProcs = &customProcs;
  1532.  
  1533.     /* Create the picture used for spooling */
  1534.     spoolPict = (PicHandle)NewHandleMargin( sizeof (Picture), kAllocApp,
  1535.             !kAllocClr );
  1536.     if (spoolPict == nil)
  1537.         Exception( &exception, 0, memFullErr );
  1538.  
  1539.     /* Skip the PICT file header */
  1540.     error = SetFPos( pictFileRef, fsFromStart, 512L );
  1541.     if (error != noErr)
  1542.         Exception( &exception, 0, error );
  1543.  
  1544.     /* Read the picture header into spoolPict */
  1545.     dataLen = sizeof (Picture);
  1546.     error = FSRead( pictFileRef, /*◊*/&dataLen, (Ptr) *spoolPict );
  1547.     if (error != noErr)
  1548.         Exception( &exception, 0, error );
  1549.  
  1550.     /* Spool the picture in */
  1551.     gPictureInfo.colors = nil;
  1552.     gPictureInfo.depth = 0;
  1553.     gPictureInfo.frame = (**spoolPict).picFrame;
  1554.     gPictFileRef = pictFileRef;
  1555.     DrawPicture( spoolPict, &gPictureInfo.frame );
  1556.  
  1557.     /* Clean up after myself */
  1558.     SetPort( savedPort );
  1559.     ClosePort( (GrafPtr)&tempPort );
  1560.     tempPortOpen = false;
  1561.     DisposeHandle( (Handle)spoolPict );
  1562.  
  1563.     /* Return picture information */
  1564.     *retPictInfo = gPictureInfo;
  1565.     return noErr;
  1566.     }
  1567.  
  1568.  
  1569. /******************************************************************************\
  1570. * NAME & SYNOPSIS:
  1571. * InfoBits: GrafProc to get information about bit/pixel maps
  1572. *
  1573. * PARAMETERS:
  1574. * BitMapPtr srcBits:  Pointer to bit/pixel map being transferred
  1575. * Rect      *srcRect: Source rectangle of CopyBits call; ignored
  1576. * Rect      *dstRect: Destination rectangle of CopyBits call; ignored
  1577. * short     mode:     Transfer mode; ignored
  1578. * RgnHandle maskRgn:  Region to mask CopyBits; ignored
  1579. *
  1580. * DEFINITION:
  1581. * InfoBits is a custom QuickDraw graphics procedure which is used to find the
  1582. * depth and color table of the deepest PixMap in the picture that’s being drawn.
  1583. * This information is returned in the gPictureInfo global variable.
  1584. *
  1585. * DESCRIPTION:
  1586. * InfoBits is called every time a CopyBits call is found in a picture that’s
  1587. * being drawn.  It first checks to see if the CopyBits call is for a PixMap or
  1588. * a BitMap.  If it’s a BitMap, the most significant bit of rowBytes is clear; if
  1589. * it’s a PixMap, the most significant byte of rowBytes is set.  If it’s a
  1590. * PixMap, its depth is compared against the depth of any previous PixMap that
  1591. * has been seen in the picture.  If it’s deeper, then the depth and color table
  1592. * of the PixMap is saved.  Otherwise, the PixMap is ignored.
  1593. *
  1594. * If srcBits is a BitMap and no PixMaps have yet been seen in the picture, then
  1595. * InfoBits remembers a depth of 1 bit per pixel and no color table.
  1596. *
  1597. * RETURN VALUES:
  1598. * gPictureInfo: Depth and color table of deepest PixMap in a picture.
  1599. \******************************************************************************/
  1600.  
  1601. pascal void InfoBits(
  1602.     BitMapPtr srcBits,  /* Pointer to bit/pixel map being transferred */
  1603.     Rect      *srcRect, /* Source rectangle of CopyBits call; ignored */
  1604.     Rect      *dstRect, /* Destination rectangle of CopyBits call; ignored */
  1605.     short     mode,     /* Transfer mode; ignored */
  1606.     RgnHandle maskRgn)  /* Region to mask CopyBits; ignored */
  1607. {
  1608. #pragma unused (srcRect,dstRect,mode,maskRgn)
  1609.     PixMapPtr srcPix; /* Handle to the picture’s PixMap */
  1610.  
  1611.     if (srcBits->rowBytes & 0x8000)
  1612.     {
  1613.         /* srcBits is a pixel map, get depth and color table */
  1614.         srcPix = (PixMapPtr)srcBits;
  1615.  
  1616.         /* Only save info if this pixel map is deepest seen so far */
  1617.         if (srcPix->pixelSize > gPictureInfo.depth)
  1618.         {
  1619.             gPictureInfo.depth = srcPix->pixelSize;
  1620.             if (srcPix->pixelType != RGBDirect)
  1621.             {
  1622.                 if (gPictureInfo.colors != nil)
  1623.                     DisposeHandle( (Handle)gPictureInfo.colors );
  1624.                 gPictureInfo.colors = srcPix->pmTable;
  1625.                 HandToHand( (Handle *)&gPictureInfo.colors );
  1626.             }
  1627.         }
  1628.     }
  1629.     else
  1630.     {
  1631.         /* srcBits is a bit map, has depth of 1 and B&W color table */
  1632.         if (gPictureInfo.depth == 0)
  1633.         {
  1634.             gPictureInfo.depth = 1;
  1635.             if (gPictureInfo.colors != nil)
  1636.                 DisposeHandle( (Handle)gPictureInfo.colors );
  1637.             gPictureInfo.colors = nil;
  1638.         }
  1639.     }
  1640. }
  1641.  
  1642.  
  1643. /******************************************************************************\
  1644. * NAME & SYNOPSIS:
  1645. * DrawFilePicture: Read a PICT file into the current GrafPort
  1646. *
  1647. * PARAMETERS:
  1648. * short pictFileRef: File reference number of PICT file to open
  1649. * Rect  *destRect:   Rectangle to draw the picture into
  1650. *
  1651. * DEFINITION:
  1652. * This routine is used to read the picture data from the PICT file specified by
  1653. * pictFileRef and draw the picture into the rectangle specified by the destRect
  1654. * parameter in the current GrafPort.  It’s just like DrawPicture, except the
  1655. * file reference number of a PICT file is specified instead of a PicHandle.
  1656. *
  1657. * DESCRIPTION:
  1658. * DrawFilePicture works by using the picture spooling method described in Inside
  1659. * Macintosh V on pages 87 through 90.  In this method, the getPicProc GrafProc is
  1660. * replaced by my routine called FileGetPic which reads PICT information from
  1661. * disk.  By doing this, the DrawPicture command effectively reads PICT files.
  1662. *
  1663. * After setting up the GrafProcs record, this routine installs the GrafProcs
  1664. * into the current port.  Then, a temporary picture is manually allocated and
  1665. * placed into spoolPict.  This is essentially a placeholder to hold the picture
  1666. * header (which is different from the 512-byte PICT file header) and is always
  1667. * the size of the Picture record type without any picture data.
  1668. *
  1669. * The PICT file is opened using HOpen.  HOpen is documented in the File Manager
  1670. * chapter of Inside Macintosh VI, but it still works on pre-7.0 machines because
  1671. * it’s just glue code that calls PBHOpen.  Because of the way the File Manager
  1672. * works, this routine also works on MFS volumes.  This routine is preferable to
  1673. * FSOpen because HOpen allows you to specify access permissions, and because it
  1674. * lets you specify a file by the more reliable volume reference number,
  1675. * directory ID, and file name rather than by working directory reference number
  1676. * and file name.  I’m using the FSSpec record to hold file information, so
  1677. * specifying a file like this is especially convenient.  If this were a 7.0-only
  1678. * application, I’d call FSpOpen instead of HOpen because FSpOpen takes an FSSpec
  1679. * record directly.
  1680. *
  1681. * After the PICT file is opened, SetFPos is called to skip the first 512 bytes
  1682. * of the file.  The first 512 bytes of a PICT file is called the PICT file
  1683. * header (not to be confused with the picture header, which contains the picSize
  1684. * and picFrame fields of the Picture type) and holds application-specific
  1685. * information.  This application doesn’t care what’s there so it just skips it.
  1686. * The picture header follows the PICT file header and is read into spoolPict.
  1687. *
  1688. * Finally (almost), DrawPicture is called.  Because this routine replaced the
  1689. * current port’s GrafProcs with my own, DrawPicture gets the picture information
  1690. * from the PICT file rather than from spoolPict.  It draws the picture into the
  1691. * current port.
  1692. *
  1693. * RETURN VALUES:
  1694. * Result:   Returns an error code.
  1695. * destRect: Returns the picFrame of the picture in the PICT file
  1696. \******************************************************************************/
  1697.  
  1698. static OSErr DrawFilePicture(
  1699.     short pictFileRef, /* File reference number of PICT file to open */
  1700.     Rect  *destRect)   /* Rectangle to draw the picture into */
  1701. {
  1702.     GrafPtr      currPort;    /* Ptr to current port used for Drawing pict */
  1703.     CQDProcs     customProcs; /* Custom GrafProcs */
  1704.     QDProcsPtr   savedProcs;  /* Current GrafPort’s QD procs; used to restore */
  1705.     PicHandle    spoolPict;   /* Picture used for spooling */
  1706.     long         dataLen;     /* Number of bytes of data to read */
  1707.     long         qdVersion;   /* Version number of QuickDraw */
  1708.     OSErr        error;
  1709.     ExceptionRec exception;
  1710.     long         errorCode;
  1711.  
  1712.     currPort = nil;
  1713.     spoolPict = nil;
  1714.     savedProcs = nil;
  1715.     error = noErr;
  1716.  
  1717.     /* Exception-handling code */
  1718.     if (ExceptionEntry( /*<*/&exception, nil, /*<*/&errorCode ))
  1719.     {
  1720.         if (spoolPict != nil)
  1721.             DisposeHandle( (Handle)spoolPict );
  1722.         if (savedProcs != nil)
  1723.             currPort->grafProcs = savedProcs;
  1724.         return errorCode;
  1725.     }
  1726.  
  1727.     /* Get the current version of QuickDraw */
  1728.     (void)Gestalt( gestaltQuickdrawVersion, /*<*/&qdVersion );
  1729.  
  1730.     /* Get a pointer to the current port and determine its type */
  1731.     GetPort( /*<*/&currPort );
  1732.  
  1733.     /* Initialize the GrafProcs for reading from a PICT file */
  1734.     if (qdVersion >= gestalt8BitQD)
  1735.         SetStdCProcs( /*<*/&customProcs );
  1736.     else
  1737.         SetStdProcs( /*<*/(QDProcsPtr) &customProcs );
  1738.     customProcs.getPicProc = gQDGetPicUPP;
  1739.  
  1740.     /* Install the custom GrafProcs into the tempPort */
  1741.     savedProcs = currPort->grafProcs;
  1742.     currPort->grafProcs = (QDProcsPtr)&customProcs;
  1743.  
  1744.     /* Create the temporary picture used for spooling */
  1745.     spoolPict = (PicHandle)NewHandleMargin( sizeof (Picture),
  1746.             kAllocApp, !kAllocClr );
  1747.     if (spoolPict == nil)
  1748.         Exception( &exception, 0, memFullErr );
  1749.  
  1750.     /* Skip the 512-byte PICT file header */
  1751.     error = SetFPos( pictFileRef, fsFromStart, kPictFileHeaderSize );
  1752.     if (error != noErr)
  1753.         Exception( &exception, 0, error );
  1754.  
  1755.     /* Read the picture header (not PICT file header) into spoolPict */
  1756.     dataLen = sizeof (Picture);
  1757.     error = FSRead( pictFileRef, /*◊*/&dataLen, (Ptr)*spoolPict );
  1758.     if (error != noErr)
  1759.         Exception( &exception, 0, error );
  1760.  
  1761.     /* Spool the picture in */
  1762.     gPictFileRef = pictFileRef;
  1763.     DrawPicture( spoolPict, destRect );
  1764.  
  1765.     /* Clean up after myself */
  1766.     DisposeHandle( (Handle)spoolPict );
  1767.     currPort->grafProcs = savedProcs;
  1768.  
  1769.     return noErr;
  1770. }
  1771.  
  1772.  
  1773. /******************************************************************************\
  1774. * NAME & SYNOPSIS:
  1775. * FileGetPic: Get QuickDraw picture information from a file
  1776. *
  1777. * PARAMETERS:
  1778. * Ptr   dataPtr:   Points to location that picture data should be placed
  1779. * short byteCount: Number of bytes of picture data to read
  1780. *
  1781. * DEFINITION:
  1782. * This routine is a custom QuickDraw graphics procedure which replaces the
  1783. * standard GetPicProc.  QuickDraw graphics procedures are routines that
  1784. * QuickDraw calls to do almost everything that it does, like drawing rectangles,
  1785. * or getting picture data.  They can be replaced by setting up a QDProcs record
  1786. * and setting the appropriate field to point to the custom QuickDraw graphics
  1787. * procedure.  In this case, the getPicProc field is set to point to FileGetPic
  1788. * so that any DrawPicture call within the GrafPort causes FileGetPic to be
  1789. * called to get picture data.  Instead of getting picture data from memory,
  1790. * FileGetPic gets picture data from the disk file whose file reference number is
  1791. * stored in the gPictFileRef global variable.
  1792. *
  1793. * DESCRIPTION:
  1794. * FileGetPic simply calls FSRead to read data from the file whose file reference
  1795. * number is stored in gPictFileRef into the area pointed to by dataPtr, which is
  1796. * an area of memory that’s set up by QuickDraw to receive picture data.  The
  1797. * number of bytes of picture data that QuickDraw wants is specified by the
  1798. * byteCount parameter.
  1799. *
  1800. * RETURN VALUES:
  1801. * None
  1802. \******************************************************************************/
  1803.  
  1804. pascal void FileGetPic(
  1805.     Ptr   dataPtr,   /* Points to location that picture data should be placed */
  1806.     short byteCount) /* Number of bytes of picture data to read */
  1807. {
  1808.     long longCount; /* Number of bytes to read from the PICT file */
  1809.  
  1810.     longCount = byteCount;
  1811.     (void)FSRead( gPictFileRef, /*◊*/&longCount, dataPtr );
  1812. }
  1813.  
  1814.  
  1815. /******************************************************************************\
  1816. * NAME & SYNOPSIS:
  1817. * OpenFilePicture: Open a picture for spooling to a file
  1818. *
  1819. * PARAMETERS:
  1820. * short pictFileRef: File reference number of PICT file to write to
  1821. * Rect  *sourceRect: Rectangle to draw the picture from
  1822. *
  1823. * DEFINITION:
  1824. * Normally, when you create a picture, you call OpenPicture, draw, then call
  1825. * ClosePicture.  But if you can create a PICT file in a similar way by calling
  1826. * OpenFilePicture and CloseFilePicture.  Almost like you would normally, you
  1827. * should set up the current port however you want it, call OpenFilePicture,
  1828. * use QuickDraw routines to draw, then call CloseFilePicture when you’re done.
  1829. * But before you call OpenFilePicture, you must open the PICT file for writing,
  1830. * and pass the file’s reference number in the pictFileRef parameter.  Once
  1831. * CloseFilePicture is called, the PICT file is complete.
  1832. *
  1833. * DESCRIPTION:
  1834. * OpenFilePicture installs the FilePutPic custom graphics procedure by setting
  1835. * up a QDprocs record with the default pointers, and then setting the putPicProc
  1836. * field to point to the FilePutPic function.  This QDProcs record is then
  1837. * installed into the current GrafPort.  Then, an empty picture is allocated just
  1838. * to have something to pass to the real OpenPicture routine.  Then the 512-byte
  1839. * PICT file header is written.  This program doesn’t do anything with those 512
  1840. * bytes, so only zeroes are written.  Also, the ten-byte picture header is
  1841. * written with zeroes for now; it’ll get the real picture header after the PICT
  1842. * file data is written.  Finally, the global variables are set up so that
  1843. * FilePutPic can reach them.
  1844. *
  1845. * RETURN VALUES:
  1846. * Result: Error code of any error that occurs.
  1847. \******************************************************************************/
  1848.  
  1849. static OSErr OpenFilePicture(
  1850.     short pictFileRef, /* File reference number of PICT file to open */
  1851.     Rect  *sourceRect) /* Rectangle to draw the picture from */
  1852. {
  1853.     GrafPtr      currPort;    /* Ptr to current port used for Drawing pict */
  1854.     long         dataLen;     /* Number of bytes of data to write */
  1855.     short        zeroData;    /* Equal to 0; used to write PICT file header */
  1856.     short        count;       /* Generic counter */
  1857.     OSErr        error;
  1858.     ExceptionRec exception;
  1859.     long         errorCode;
  1860.  
  1861.     currPort = nil;
  1862.     gSpoolPicture = nil;
  1863.     gSavedProcs = nil;
  1864.     error = noErr;
  1865.  
  1866.     /* Exception-handling code */
  1867.     if (ExceptionEntry( /*<*/&exception, nil, /*<*/&errorCode ))
  1868.     {
  1869.         if (gSpoolPicture != nil)
  1870.             DisposeHandle( (Handle)gSpoolPicture );
  1871.         if (gSavedProcs != nil)
  1872.             currPort->grafProcs = gSavedProcs;
  1873.         return errorCode;
  1874.     }
  1875.  
  1876.     /* Get a pointer to the current port */
  1877.     GetPort( /*<*/&currPort );
  1878.  
  1879.     /* Initialize the GrafProcs for reading from a PICT file */
  1880.     if (currPort->portBits.rowBytes & 0x8000)
  1881.         SetStdCProcs( /*<*/&gCustomProcs );
  1882.     else
  1883.         SetStdProcs( /*<*/(QDProcsPtr)&gCustomProcs );
  1884.     gCustomProcs.putPicProc = gQDPutPicUPP;
  1885.  
  1886.     /* Install the custom GrafProcs into the tempPort */
  1887.     gSavedProcs = currPort->grafProcs;
  1888.     currPort->grafProcs = (QDProcsPtr)&gCustomProcs;
  1889.  
  1890.     /* Create the temporary picture used for spooling */
  1891.     gSpoolPicture = (PicHandle)NewHandleMargin( sizeof (Picture),
  1892.             kAllocApp, kAllocClr );
  1893.     if (gSpoolPicture == nil)
  1894.         Exception( &exception, 0, memFullErr );
  1895.  
  1896.     /* Make sure we’re positioned at the beginning of the file */
  1897.     error = SetFPos( pictFileRef, fsFromStart, 0 );
  1898.     if (error != noErr)
  1899.         Exception( &exception, 0, error );
  1900.  
  1901.     /* Zero the 512-byte PICT file header */
  1902.     dataLen = sizeof (short);
  1903.     zeroData = 0;
  1904.     for (count = 0; count < (kPictFileHeaderSize + sizeof (Picture))
  1905.             / sizeof (short); ++count)
  1906.     {
  1907.         error = FSWrite( pictFileRef, /*◊*/&dataLen, &zeroData );
  1908.         if (error != noErr)
  1909.             Exception( &exception, 0, error );
  1910.     }
  1911.  
  1912.     /* Begin spooling the picture out */
  1913.     gPictFileRef = pictFileRef;
  1914.     gPictureSize = sizeof (Picture);
  1915.     gSpoolPicture = nil;
  1916.     gSpoolPicture = OpenPicture( sourceRect );
  1917.  
  1918.     return noErr;
  1919. }
  1920.  
  1921.  
  1922. /******************************************************************************\
  1923. * NAME & SYNOPSIS:
  1924. * CloseFilePicture: Close a spooled picture (don’t close the PICT file)
  1925. *
  1926. * PARAMETERS:
  1927. * None
  1928. *
  1929. * DEFINITION:
  1930. * ClosePictureFile is called to complete the writing of a PICT file.  It’s
  1931. * called the same way you’d call ClosePicture.
  1932. *
  1933. * DESCRIPTION:
  1934. * ClosePicture is called to close the currently-open picture, then the picture
  1935. * header is written with the final values.  Because the temporary picture isn’t
  1936. * needed anymore, KillPicture is called to get rid of it.  Finally, the current
  1937. * port’s graphics procedures are restored to what they were before
  1938. * OpenFilePicture was called.
  1939. *
  1940. * RETURN VALUES:
  1941. * Result: Error code of any error that occurs.
  1942. \******************************************************************************/
  1943.  
  1944. static OSErr CloseFilePicture()
  1945. {
  1946.     long         dataLen;  /* Number of bytes of data to write */
  1947.     GrafPtr      currPort; /* Pointer to the current GrafPort */
  1948.     OSErr        error;
  1949.     ExceptionRec exception;
  1950.     long         errorCode;
  1951.  
  1952.     error = noErr;
  1953.  
  1954.     /* Exception-handling code */
  1955.     if (ExceptionEntry( /*<*/&exception, nil, /*<*/&errorCode ))
  1956.     {
  1957.         return errorCode;
  1958.     }
  1959.  
  1960.     /* Close the spooled picture */
  1961.     ClosePicture();
  1962.  
  1963.     /* Make sure we’re positioned just past the PICT file header */
  1964.     error = SetFPos( gPictFileRef, fsFromStart, kPictFileHeaderSize );
  1965.     if (error != noErr)
  1966.         Exception( &exception, 0, error );
  1967.  
  1968.     /* Write the picture header (NOT the PICT file header!) */
  1969.     dataLen = sizeof (Picture);
  1970.     error = FSWrite( gPictFileRef, /*◊*/&dataLen, (Ptr)*gSpoolPicture );
  1971.     if (error != noErr)
  1972.         Exception( &exception, 0, error );
  1973.  
  1974.     /* Dispose of the picture */
  1975.     KillPicture( gSpoolPicture );
  1976.  
  1977.     /* Restore the previous graphics procedures */
  1978.     GetPort( /*<*/&currPort);
  1979.     currPort->grafProcs = gSavedProcs;
  1980. }
  1981.  
  1982.  
  1983. /******************************************************************************\
  1984. * NAME & SYNOPSIS:
  1985. * FilePutPic: Write QuickDraw picture information to PICT file
  1986. *
  1987. * PARAMETERS:
  1988. * Ptr   dataPtr:   Points to location of picture data
  1989. * short byteCount: Number of bytes of picture data to write
  1990. *
  1991. * DEFINITION:
  1992. * This routine is a custom QuickDraw graphics procedure which replaces the
  1993. * standard PutPicProc.  QuickDraw graphics procedures are routines that
  1994. * QuickDraw calls to do almost everything that it does, like drawing rectangles,
  1995. * or getting picture data.  They can be replaced by setting up a QDProcs record
  1996. * and setting the appropriate field to point to the custom QuickDraw graphics
  1997. * procedure.  In this case, the getPicProc field is set to point to FileGetPic
  1998. * so that any drawing call within the GrafPort while a picture is open causes
  1999. * FilePutPic to be called to write picture data.  Instead of writing picture
  2000. * data to memory, FileGetPic writes picture data to the disk file whose file
  2001. * reference number is stored in the gPictFileRef global variable.
  2002. *
  2003. * DESCRIPTION:
  2004. * FileGetPic calls FSWrite to write data t=o the file whose file reference
  2005. * number is stored in gPictFileRef from the area pointed to by dataPtr, which is
  2006. * an area of memory that’s set up by QuickDraw to hold picture data.  The
  2007. * number of bytes of picture data that QuickDraw has is specified by the
  2008. * byteCount parameter.  Also, the picSize field of gSpoolPicture is updated so
  2009. * that QuickDraw can keep track of the current size of the picture.
  2010. *
  2011. * RETURN VALUES:
  2012. * None
  2013. \******************************************************************************/
  2014.  
  2015. pascal void FilePutPic(
  2016.     Ptr   dataPtr,   /* Points to location of picture data */
  2017.     short byteCount) /* Number of bytes of picture data to write */
  2018. {
  2019.     long dataLen; /* Number of bytes to write the PICT file */
  2020.  
  2021.     dataLen = byteCount;
  2022.     gPictureSize += dataLen;
  2023.     (void)FSWrite( gPictFileRef, /*◊*/&dataLen, dataPtr );
  2024.     if (gSpoolPicture != nil)
  2025.         (**gSpoolPicture).picSize = gPictureSize;
  2026. }
  2027.